/*
 * Modbus.h
 *
 *  Created on: May 5, 2020
 *      Author: Alejandro Mera
 */

#ifndef THIRD_PARTY_MODBUS_INC_MODBUS_H_
#define THIRD_PARTY_MODBUS_INC_MODBUS_H_


#include "ModbusConfig.h"
#include <inttypes.h>
#include <stdbool.h>
#include "usbd_cdc_if.h"

#define printf usb_printf

// 用于数据的类型转换
typedef union
{
	uint8_t u8[4];
	uint16_t u16[2];
	uint32_t u32;
} bytesFields;


//Modbus主机从机定义
typedef enum
{
    MB_SLAVE = 3,
    MB_MASTER = 4
}mb_masterslave_t ;

/**
 * @enum MB_FC
 * @brief
 * Modbus function codes summary.
 * These are the implement function codes either for Master or for Slave.
 * Modbus功能码定义
 *
 * @see also fctsupported
 * @see also modbus_t
 */
typedef enum MB_FC
{
    MB_FC_READ_COILS               = 0x01,	 /*!< FCT=1 -> read coils or digital outputs */
    MB_FC_READ_DISCRETE_INPUT      = 0x02,	 /*!< FCT=2 -> read digital inputs */
    MB_FC_READ_REGISTERS           = 0x03,	 /*!< FCT=3 -> read registers or analog outputs */
    MB_FC_READ_INPUT_REGISTER      = 0x04,	 /*!< FCT=4 -> read analog inputs */
    MB_FC_WRITE_COIL               = 0x05,	 /*!< FCT=5 -> write single coil or output */
    MB_FC_WRITE_REGISTER           = 0x06,	 /*!< FCT=6 -> write single register */
    MB_FC_WRITE_MULTIPLE_COILS     = 0x0f,   /*!< FCT=15 -> write multiple coils or outputs */
    MB_FC_WRITE_MULTIPLE_REGISTERS = 0x10	 /*!< FCT=16 -> write multiple registers */
}mb_functioncode_t;

//数据接收循环队列
typedef struct
{
uint8_t uxBuffer[MAX_BUFFER];
uint8_t u8start;
uint8_t u8end;
uint16_t u16available;
bool    overflow;
} modbusRingBuffer_t;

//通信状态
typedef enum COM_STATES
{
    COM_IDLE                     = 0,
    COM_WAITING                  = 1,
    COM_PROCESSING               = 2
}mb_com_state_t;
 
 //错误列表
typedef enum ERR_LIST
{
    ERR_NOT_MASTER                = -1,
    ERR_POLLING                   = -2,
    ERR_BUFF_OVERFLOW             = -3,
    ERR_BAD_CRC                   = -4,
    ERR_EXCEPTION                 = -5,
    ERR_BAD_SIZE                  = -6,
    ERR_BAD_ADDRESS               = -7,
    ERR_TIME_OUT		          = -8,
    ERR_BAD_SLAVE_ID		      = -9,
	ERR_BAD_TCP_ID		          = -10,
	ERR_OK_QUERY				  = -11

}mb_errot_t;

//特殊情况
enum
{
    EXC_FUNC_CODE = 1,  //功能码超出范围
    EXC_ADDR_RANGE = 2, //地址超出范围
    EXC_REGS_QUANT = 3, //返回帧大小超出范围
    EXC_EXECUTE = 4
};

/**
 * @struct modbus_t
 * @brief
 * 主机的数据请求格式:TODO:请求的状态反馈
 * Master query structure:
 * This structure contains all the necessary fields to make the Master generate a Modbus query.
 * A Master may keep several of these structures and send them cyclically or
 * use them according to program needs.
 */
typedef struct
{
    uint8_t u8id;          /*!< 从机地址Slave address between 1 and 247. 0 means broadcast */
    mb_functioncode_t u8fct;         /*!< 功能码Function code: 1, 2, 3, 4, 5, 6, 15 or 16 */
    uint16_t u16RegAdd;    /*!< 地址Address of the first register to access at slave/s */
    uint16_t u16CoilsNo;   /*!< 数量Number of coils or registers to access */
    uint16_t *u16reg;     /*!< 返回数据的存储地址Pointer to memory image in master */
}
modbus_t;


/**
 * @struct modbusHandler_t
 * @brief
 * Modbus handler structure
 * Contains all the variables required for Modbus daemon operation
 */
typedef struct
{
    //属性信息
	mb_masterslave_t uModbusType;
	uint8_t u8id; //!< 0=master, 1..247=slave number
	uint16_t u16timeOut;//定义超时时间，比如主机等待超过u16timeOut或者超过宏定义TIMEOUT_MODBUS则视为超时
    //状态信息
	mb_errot_t i8lastError;//!< 上一个报错信息，TODO：实现错误处理回调函数
	int8_t i8state;//主机中用来标记空闲和等待数据的状态，从机中用来标记回复主机的数据长度
	uint16_t u16InCnt, u16OutCnt, u16errCnt; //Modbus通信的统计量，输入帧数量，输出帧数量和错误数量
    uint32_t time_tick; //用于当前请求的响应计时
    //数据处理buffer
	uint8_t u8Buffer[MAX_BUFFER]; //Modbus用于数据处理的buffer
	uint8_t u8BufferSize;//Modbus buffer的大小
    //寄存器
	uint16_t *u16regs;//寄存器地址，从机为一个固定地址，主机中该地址由telegram给定
	uint16_t u16regsize;//寄存器大小,从机中将进行地址大小判别
    //数据接收队列
	modbusRingBuffer_t xBufferRX;//环形队列用于存放接收到的原始数据
}
modbusHandler_t;

// 用户函数
void ModbusReg(modbusHandler_t * modH);//Modbus主机从机注册函数

void ModbusQueryClean(modbusHandler_t * modH); //清除请求列表
bool ModbusQueryPush(modbusHandler_t * modH, modbus_t telegram); // 发送一个请求到请求列表,由主机进行请求并等待数据,只有主机才能进行请求

// 服务函数
void TaskModbusMaster(void *argument); //master, 参数为(modbusHandler_t *)类型, 推荐使用1ms运行周期
void TaskModbusSlave(void *argument); //slave, 参数为(modbusHandler_t *)类型, 推荐每当一帧数据接收完毕后调用一次
void TaskModbusMonitor(void *argument); //数据打印任务，可以较长时间打印一次

// 收发底层接口函数
void ModbusUartSend(modbusHandler_t *modH, const uint8_t *data, uint8_t len); //weak接口函数,用于串口数据发送，用户在其他文件内自行实现
void ModbusUartRecv(modbusHandler_t *modH, uint8_t byte); //Modbus字节接收函数
void ReceiveComplete(modbusHandler_t *modH);  //放到接收空闲中断中，每当一帧数据接收完毕后调用一次

// 回调函数
void modbusErrorCallback(modbusHandler_t* modH);// 回调函数，modbus报错时将进入,用户可以重新实现


/* prototypes of the original library not implemented
uint16_t getInCnt(); //!<number of incoming messages
uint16_t getOutCnt(); //!<number of outcoming messages
uint16_t getErrCnt(); //!<error counter
uint8_t getID(); //!<get slave ID between 1 and 247
uint8_t getState();
uint8_t getLastError(); //!<get last error message
void setID( uint8_t u8id ); //!<write new ID for the slave
void setTxendPinOverTime( uint32_t u32overTime );
void ModbusEnd(); //!<finish any communication and release serial communication port

*/


#endif /* THIRD_PARTY_MODBUS_INC_MODBUS_H_ */
